package it.polimi.jita.cp.block;

import it.polimi.jita.cp.block.co.CapacityOptimizatorInputBuilder;
import it.polimi.jita.cp.block.dd.DynamicDecisionMakerInputBuilder;
import it.polimi.jita.cp.block.de.DemandEstimatorInputBuilder;
import it.polimi.jita.cp.block.pc.PenaltyCalculatorInputBuilder;
import it.polimi.jita.cp.block.pc.PenaltyType;
import it.polimi.jita.cp.block.scheduler.SchedulerInputBuilder;
import it.polimi.jita.cp.rs.RanStressInputBuilder;

import java.util.List;
import java.util.Set;

import org.apache.commons.cli2.CommandLine;
import org.apache.commons.cli2.Group;
import org.apache.commons.cli2.Option;
import org.apache.commons.cli2.builder.ArgumentBuilder;
import org.apache.commons.cli2.builder.CommandBuilder;
import org.apache.commons.cli2.builder.DefaultOptionBuilder;
import org.apache.commons.cli2.builder.GroupBuilder;
import org.apache.commons.cli2.commandline.Parser;
import org.apache.commons.cli2.option.Command;
import org.apache.commons.cli2.validation.InvalidArgumentException;
import org.apache.commons.cli2.validation.Validator;

public class CliParser {
	private InputBuilderRegister inputBuilderRegister = new InputBuilderRegister();
	private Parser parser;

	public CliParser() {

		Command estimate = createEstimateCommand();
		Command penalty = createPenaltyCommand();
		Command optimize = createOptimizeCommand();
		Command schedule = createScheduleCommand();
		Command ranStress = createRanStressCommand();
		Command dynamicDecision = createDynamicDecisionCommand();

		inputBuilderRegister.registerOption(estimate,
				DemandEstimatorInputBuilder.class);
		inputBuilderRegister.registerOption(penalty,
				PenaltyCalculatorInputBuilder.class);
		inputBuilderRegister.registerOption(optimize,
				CapacityOptimizatorInputBuilder.class);
		inputBuilderRegister.registerOption(schedule,
				SchedulerInputBuilder.class);
		inputBuilderRegister.registerOption(ranStress,
				RanStressInputBuilder.class);
		inputBuilderRegister.registerOption(dynamicDecision,
				DynamicDecisionMakerInputBuilder.class);

		parser = new Parser();
		parser.setGroup(createGroupOption());

	}

	public Input parse(String[] args) throws InstantiationException,
			IllegalAccessException, Exception {
		CommandLine cl = parser.parse(args);
		Command command = (Command) cl.getOptions().get(0);
		Input input = inputBuilderRegister.getInputBuilder(command)
				.createInput(cl);
		return input;
	}

	private Group createGroupOption() {
		GroupBuilder gb = new GroupBuilder();
		Set<Option> command = inputBuilderRegister.getOptions();
		for (Option option : command) {
			gb = gb.withOption(option);
		}
		return gb.create();
	}

	private Command createPenaltyCommand() {
		CommandBuilder cb = new CommandBuilder();
		DefaultOptionBuilder ob = new DefaultOptionBuilder();
		GroupBuilder gb = new GroupBuilder();
		ArgumentBuilder ab = new ArgumentBuilder();

		Option input = ob
				.withLongName("input")
				.withShortName("i")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("input.csv").create()).create();

		Option headers = ob.withLongName("headers").withShortName("h").create();
		Option showCharts = ob.withLongName("show-charts").withShortName("-sc")
				.create();
		Option output = ob
				.withLongName("output")
				.withShortName("o")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("output.csv").create()).create();
		Option x0 = ob.withLongName("x0").withShortName("x0")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();
		Option x1 = ob.withLongName("x1").withShortName("x1")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();
		Option x2 = ob.withLongName("x2").withShortName("x2")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.create();
		Option numVCpu = ob.withLongName("num-vcpu").withShortName("nc")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.create();
		Option systemCapacity = ob.withLongName("system-capacity")
				.withShortName("sc")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();

		Group optsGroup = gb.withName("Penalty").withOption(input)
				.withOption(output).withOption(x0).withOption(x1)
				.withOption(x2).withOption(numVCpu).withOption(systemCapacity)
				.withOption(showCharts).withOption(headers).create();

		return cb.withName("penalty").withChildren(optsGroup).create();

	}

	private Command createOptimizeCommand() {
		CommandBuilder cb = new CommandBuilder();
		DefaultOptionBuilder ob = new DefaultOptionBuilder();
		GroupBuilder gb = new GroupBuilder();
		ArgumentBuilder ab = new ArgumentBuilder();

		Option input = ob
				.withLongName("input")
				.withShortName("i")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("input.csv").create()).create();
		Option output = ob
				.withLongName("output")
				.withShortName("o")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("output.csv").create()).create();

		Option headers = ob.withLongName("headers").withShortName("h").create();
		Option showCharts = ob.withLongName("show-charts").withShortName("sc")
				.create();

		Option x0 = ob.withLongName("x0").withShortName("x0")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();
		Option x1 = ob.withLongName("x1").withShortName("x1")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();
		Option x2 = ob.withLongName("x2").withShortName("x2")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.create();
		Option numVCpu = ob.withLongName("num-vcpu").withShortName("nc")
				.withRequired(true)
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.create();

		Group penaltyParams = gb.withName("penalty").withOption(x0)
				.withOption(x1).withOption(x2).create();

		Option penalty = ob
				.withLongName("penalty")
				.withShortName("p")
				.withArgument(
						ab.withMinimum(1).withMaximum(1)
								.withDefault("asymptotic")
								.withValidator(new Validator() {
									@Override
									public void validate(
											@SuppressWarnings("rawtypes") List arg0)
											throws InvalidArgumentException {
										String arg = (String) arg0.get(0);
										try {
											PenaltyType.valueOf(arg
													.toUpperCase());
										} catch (Exception e) {
											throw new InvalidArgumentException(
													e.getMessage());
										}

									}
								}).create()).withChildren(penaltyParams)
				.create();
		Option sampling = ob
				.withLongName("sampling")
				.withShortName("s")
				.withArgument(
						ab.withDefault(1.0).withMinimum(1).withMaximum(1)
								.create()).withRequired(true).create();
		Option max_capacity_demand = ob
				.withLongName("max-demand")
				.withShortName("md")
				.withArgument(
						ab.withDefault(1.0).withMinimum(1).withMaximum(1)
								.create()).withRequired(true).create();

		Option max_penalty = ob
				.withLongName("max-penalty")
				.withShortName("mp")
				.withArgument(
						ab.withDefault(1.0).withMinimum(1).withMaximum(1)
								.create()).withRequired(true).create();

		Group optsGroup = gb.withName("Penalty").withOption(input)
				.withOption(output).withOption(numVCpu).withOption(penalty)
				.withOption(max_capacity_demand).withOption(max_penalty)
				.withOption(showCharts).withOption(headers)
				.withOption(sampling).create();

		return cb.withName("optimize").withChildren(optsGroup).create();

	}

	private Command createEstimateCommand() {
		CommandBuilder cb = new CommandBuilder();
		DefaultOptionBuilder ob = new DefaultOptionBuilder();
		GroupBuilder gb = new GroupBuilder();
		ArgumentBuilder ab = new ArgumentBuilder();

		Option input = ob
				.withLongName("input")
				.withShortName("i")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("input.csv").create()).create();

		Option output = ob
				.withLongName("output")
				.withShortName("o")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("output.csv").create()).create();

		Option headers = ob.withLongName("headers").withShortName("h").create();
		Option showCharts = ob.withLongName("show-charts").withShortName("sc")
				.create();
		Option sampling = ob
				.withLongName("sampling")
				.withShortName("s")
				.withArgument(
						ab.withDefault(1.0).withMinimum(1).withMaximum(1)
								.create()).withRequired(true).create();
		Option vcpuNum = ob
				.withLongName("vcpunum")
				.withShortName("vcpu")
				.withArgument(
						ab.withDefault(1).withMinimum(1).withMaximum(1)
								.create()).create();

		Group optsGroup = gb.withName("Estimate").withOption(input)
				.withOption(output).withOption(showCharts).withOption(headers)
				.withOption(sampling).withOption(vcpuNum).create();

		return cb.withName("estimate").withChildren(optsGroup).create();
	}

	private Command createScheduleCommand() {
		CommandBuilder cb = new CommandBuilder();
		DefaultOptionBuilder ob = new DefaultOptionBuilder();
		GroupBuilder gb = new GroupBuilder();
		ArgumentBuilder ab = new ArgumentBuilder();

		Option input = ob
				.withLongName("input")
				.withShortName("i")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("input.csv").create()).create();

		Option output = ob
				.withLongName("output")
				.withShortName("o")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("output.csv").create()).create();

		Option headers = ob.withLongName("headers").withShortName("h").create();
		Option showCharts = ob.withLongName("show-charts").withShortName("sc")
				.create();
		Option systemFrequency = ob.withLongName("system-frequency")
				.withShortName("sf")
				.withArgument(ab.withMinimum(1).withMaximum(1).create())
				.withRequired(true).create();

		Group optsGroup = gb.withName("Schedule").withOption(input)
				.withOption(output).withOption(showCharts).withOption(headers)
				.withOption(systemFrequency).create();

		return cb.withName("schedule").withChildren(optsGroup).create();
	}

	private Command createRanStressCommand() {
		CommandBuilder cb = new CommandBuilder();
		return cb.withName("ranstress").create();
	}

	private Command createDynamicDecisionCommand() {
		CommandBuilder cb = new CommandBuilder();
		DefaultOptionBuilder ob = new DefaultOptionBuilder();
		GroupBuilder gb = new GroupBuilder();
		ArgumentBuilder ab = new ArgumentBuilder();

		Option input = ob
				.withLongName("input")
				.withShortName("i")
				.withArgument(
						ab.withName("file").withMinimum(1).withMaximum(1)
								.withDefault("input.json").create()).create();

		Option output = ob
				.withLongName("output")
				.withShortName("o")
				.withArgument(
						ab.withName("file").withMinimum(1)
								.withDefault("output.csv").create()).create();

		Option sampling = ob
				.withLongName("sampling")
				.withShortName("s")
				.withArgument(
						ab.withDefault(1.0).withMinimum(1).withMaximum(1)
								.create()).withRequired(true).create();

		Option headers = ob.withLongName("headers").withShortName("h").create();
		Option showCharts = ob.withLongName("show-charts").withShortName("sc")
				.create();

		Group optsGroup = gb.withName("DDecision").withOption(input)
				.withOption(output).withOption(sampling).withOption(showCharts)
				.withOption(headers).create();

		return cb.withName("ddecision").withChildren(optsGroup).create();
	}

}
